package com.alibaba.dubbo.common.io;

import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;

public class UnsafeChunkByteArrayInputStream extends InputStream{
 
	protected LinkedList<UnsafeChunk> mData;
	protected int mPosition, mLimit, mMark = 0;
	protected int mChunkSize = 0;

	public UnsafeChunkByteArrayInputStream(LinkedList<UnsafeChunk> buffer)
	{
		if(buffer == null) throw new IllegalArgumentException();
		mLimit = 0;
		mChunkSize = 0;
		for(int i = 0;i<buffer.size();i++){
			UnsafeChunk chunk = buffer.get(i);
			if(chunk == null) throw new IllegalArgumentException("chunk is null");
			byte []data = chunk.data();
			if(data == null || data.length == 0 
					|| chunk.size() == 0
					|| chunk.size() > data.length) throw new IllegalArgumentException("chunk data error");
			if(chunk.size() != data.length && i < (buffer.size() -1)){
				throw new IllegalArgumentException("only last chunk can not be full");
			}
			mLimit += chunk.size();
			if(mChunkSize == 0) mChunkSize = data.length;
			if(data.length != mChunkSize) throw new IllegalArgumentException("chunk length must be equal");
		}
		this.mData = buffer;
    	mPosition = mMark = 0;
    }

	public int read()
	{
		if(mPosition >= mLimit) return -1;
		int ch = mPosition / this.mChunkSize;
		int off = mPosition % mChunkSize;
		mPosition ++;
		UnsafeChunk chunk = this.mData.get(ch);
		return chunk.data()[off] & 0xff;
    }

	public int read(byte b[], int off, int len)
	{
		if( b == null )
		    throw new NullPointerException();
		if( off < 0 || len < 0 || len > b.length - off )
		    throw new IndexOutOfBoundsException();
		if( mPosition >= mLimit )
		    return -1;
		if( mPosition + len > mLimit )
		    len = mLimit - mPosition;
		if( len <= 0 )
		    return 0;
		
		int boff = off;
		int xlen = len;
		while(xlen > 0){
			int ch = mPosition / this.mChunkSize;
			int offset = mPosition % mChunkSize;
			UnsafeChunk chunk = mData.get(ch);
			int read = Math.min(xlen,  chunk.size() - offset);
			System.arraycopy(chunk.data(), offset, b, boff, read);
			mPosition += read;
			boff += read;
			xlen -= read;
		}
		
		return len;
    }

	public long skip(long len)
    {
		if( mPosition + len > mLimit )
			len = mLimit - mPosition;
		if( len <= 0 )
			return 0;
		mPosition += len;
		return len;
    }

	public int available()
	{
		return mLimit - mPosition;
	}

	public boolean markSupported()
    {
    	return true;
	}

	public void mark(int readAheadLimit)
	{
		mMark = mPosition;
	}

	public void reset()
	{
		mPosition = mMark;
	}

	public void close() throws IOException
	{
		this.mData.clear();
		this.mData = null;
		this.mPosition = 0;
		this.mLimit = 0;
		this.mMark = 0;
	}

	public int position()
	{
		return mPosition;
	}

	public void position(int newPosition)
	{
		mPosition = newPosition;
	}
	
	public int size() {
		return mLimit;
	}
}
